/*******************************************************************************
 * Copyright（C） CETC-32
 * @file:AccessRule.java
 * @Description：
 * @Author ：徐文远
 * @version：1.0
 * @date ： 2021/4/22 下午8:30
 ******************************************************************************/

package com.cetc32.webutil.client.bean;

import org.apache.commons.lang3.StringUtils;

import java.util.HashSet;
public class AccessRule {
    /**
     * 权限编码
     * */
    String code;
    /**
     * 可访问具体url
     * */
    HashSet<String> access = new HashSet<>();
    /**
     * 不可访问具体url
     * */
    HashSet<String> denies = new HashSet<>();
    /**
     * 可访问具体url模式匹配列表
     * */
    HashSet<String> matchAccess = new HashSet<>();
    /**
     * 可访问具体url模式匹配列表
     * */
    HashSet<String> matchDenies = new HashSet<>();

    /**
     * 管理员权限标记
     * */
    boolean isAdmin = false;
    /**
     * 无参构造函数
     * */
    public AccessRule(){

    }

    /**
     * 有参数构造函数
     * @param code 权限码
     * */
    public AccessRule(String code){
        this.code=code;
    }

    public String getCode() {
        return code;
    }
    /**
     * 根据rule串对应实体对象
     * 添加允许访问规则
     * @param rule 控制规则
     * */
    public static AccessRule addAccessRule(String sysCode,String rule){
        if(StringUtils.isNotBlank(rule)){
            if(rule.contains("@")){
                String[] rules=rule.split("@");
                AccessRule r = new AccessRule(sysCode+rules[0]);
                r.addAccess(rules[1]);
                return r;
            }
        }
        return null;
    }

    /**
     * 根据rule串对应实体对象
     * 添加不允许访问规则
     * @param rule 控制规则
     * */
    public static AccessRule addDeniesRule(String sysCode,String rule){
        if(StringUtils.isNotBlank(rule)){
            if(rule.contains("!")){
                String[] rules=rule.split("!");
                AccessRule r = new AccessRule(sysCode+rules[0]);
                r.addDenies(rules[1]);
                return r;
            }
        }
        return null;
    }
    /**
     * 将给定的AccessRule实体的权限追加到当前对象
     * @param ar 带追加的权限信息实体
     * */
    public void addPermission(AccessRule ar){
        if(ar!=null){
            this.access.addAll(ar.getAccess());
            this.denies.addAll(ar.getDenies());
            this.matchAccess.addAll(ar.getMatchAccess());
            this.matchDenies.addAll(ar.getMatchDenies());
        }
    }

    public void setCode(String code) {
        this.code = code;
    }

    public HashSet<String> getAccess() {
        return access;
    }

    public void setAccess(HashSet<String> access) {
        this.access = access;
    }

    public HashSet<String> getDenies() {
        return denies;
    }

    public void setDenies(HashSet<String> denies) {
        this.denies = denies;
    }
    public void addRole(String roles){

    }

    public HashSet<String> getMatchAccess() {
        return matchAccess;
    }

    public void setMatchAccess(HashSet<String> matchAccess) {
        this.matchAccess = matchAccess;
    }

    public HashSet<String> getMatchDenies() {
        return matchDenies;
    }

    public void setMatchDenies(HashSet<String> matchDenies) {
        this.matchDenies = matchDenies;
    }

    /**
     * 校验指定url当前Rule是否允许，如果url既不再允许规则中，又不在拒绝规则中则认为允许返回并返回true
     * @param url 待校验的url
     * @return 返回是否校验成功，true表示有访问权限，false 表示没有访问权限
     * */
    public boolean hasPermission(String url){
        if(isAdmin)
            return true;
        if(isAccess( url))
            return true;
        if(isDenied( url))
            return false;
        return true;
    }
    /**
     * 校验当前url 是否允许访问
     * @param url 待校验的url
     * @return true表示允许访问，false 表示不允许访问
     * */
    public boolean isAccess(String url){
        return hasUrl(url, access, matchAccess);
    }
    /**
     * 校验指定url是否再对应权限集合中匹配到
     * @param url  待匹配的url
     * @param access 权限url结合
     * @param matchAccess 模式匹配集合
     * */
    private boolean hasUrl(String url, HashSet<String> access, HashSet<String> matchAccess) {
        if(access.contains(url))
            return true;
        if(!matchAccess.isEmpty()){
            url = url.substring(0,url.lastIndexOf("/"));
            if(matchAccess.contains(url+"/*"))
                return true;
            while(StringUtils.isNotBlank(url)){
                url = url.substring(0,url.lastIndexOf("/"));
                if(matchAccess.contains(url+"/**"))
                    return true;
            }
        }
        return false;
    }

    /**
     * 校验当前url 是否不允许访问
     * @param url 待校验的url
     * @return true表示不允许访问，false 表示允许访问
     * */
    public boolean isDenied(String url){
        return hasUrl(url, denies, matchDenies);
    }
    /**
     * 添加允许访问url到集合
     * 根据url进行判断，如果包含逗号则认为包含多个url则切分单独添加
     * 如果切分后url中包含*则添加到模式匹配结合否则添加的url集合
     * 针对url =“/**” 的则特殊处理，认为是管理员
     * @param url 待添加的url
     * @return 返回当前对象
     * */
    public AccessRule addAccess(String url){
        if(StringUtils.isNotBlank(url)){
            if(url.contains(",")){
                String[] urls =url.split(",");
                for(String u:urls){
                    if(u.contains("*")){
                        if(u.equals("/**"))
                            isAdmin = true;
                        this.matchAccess.add(u);
                    }else{
                        this.access.add(u);
                    }
                }
            }else{
                if(url.contains("*")){
                    if(url.equals("/**"))
                        isAdmin = true;
                    this.matchAccess.add(url);
                }else{
                    this.access.add(url);
                }
            }
        }
        return this;
    }
    /**
     * 添加不允许访问url到集合
     * 根据url进行判断，如果包含逗号则认为包含多个url则切分单独添加
     * 如果切分后url中包含*则添加到模式匹配结合否则添加的url集合
     * @param url 待添加的url
     * @return 返回当前对象
     * */
    public AccessRule addDenies(String url){
        if(StringUtils.isNotBlank(url)){
            if(url.contains(",")){
                String[] urls =url.split(",");
                for(String u:urls){
                    if(u.contains("*")){
                        this.matchDenies.add(u);
                    }else{
                        this.denies.add(u);
                    }
                }
            }else{
                if(url.contains("*")){
                    this.matchDenies.add(url);
                }else{
                    this.denies.add(url);
                }
            }
        }
        return this;
    }
    /**
     * 重写hashCode 方法
     * */
    @Override
    public int hashCode() {
        return code.hashCode();
    }

    /**
     * 重写equals 方法
     * */
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof AccessRule){
            AccessRule a=(AccessRule)obj;
            if(a.code.equals(code))
                return true;
        }
        return false;
    }
}
